package com.zdh.strategy.kit;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public abstract class NumKit {
    private static final Random random = new Random();

    public static int randomInt(int min, int max) {
        return random.nextInt(max - min) + min;
    }

    public static BigDecimal randomNumber(BigDecimal min, BigDecimal max, BigDecimal step) {
        if (max.compareTo(min) < 0) {
            return max;
        }
        if (min.compareTo(max) == 0) {
            return min;
        }
        BigDecimal a = min.add(
                (max.subtract(min)).multiply(BigDecimal.valueOf(new Random().nextDouble()))
        );
        BigDecimal b = a.divide(step, 0, RoundingMode.HALF_UP).multiply(step);
        if (b.compareTo(BigDecimal.ZERO) == 0) {
            return min;
        }
        return b;
    }

    public static BigDecimal randomNumber(BigDecimal min, BigDecimal max) {
        if (max.compareTo(min) < 0) {
            return max;
        }
        if (min.compareTo(max) == 0) {
            return min;
        }
        BigDecimal a = min.add(
                (max.subtract(min)).multiply(BigDecimal.valueOf(new Random().nextDouble()))
        );
        if (a.compareTo(BigDecimal.ZERO) == 0) {
            return min;
        }
        return a;
    }

    public static BigDecimal min(BigDecimal a, BigDecimal b) {
        if (a.compareTo(b) < 0) {
            return a;
        } else {
            return b;
        }
    }

    public static BigDecimal divide(BigDecimal a, BigDecimal b) {
        return a.divide(b, 10, RoundingMode.HALF_DOWN);
    }

    public static BigDecimal remainder(BigDecimal a, BigDecimal b) {
        return a.divideAndRemainder(b)[1];
    }

    public static BigDecimal limitStride(BigDecimal num, BigDecimal stride) {
        if (stride.compareTo(BigDecimal.ONE) == 0) {
            return num.setScale(0, RoundingMode.HALF_UP);
        }
        return num.divide(stride, 0, RoundingMode.HALF_UP).multiply(stride).stripTrailingZeros();
    }

    public static boolean gt(BigDecimal a, BigDecimal b) {
        return a.compareTo(b) > 0;
    }

    public static boolean ge(BigDecimal a, BigDecimal b) {
        return a.compareTo(b) >= 0;
    }

    public static boolean lt(BigDecimal a, BigDecimal b) {
        return a.compareTo(b) < 0;
    }

    public static boolean le(BigDecimal a, BigDecimal b) {
        return a.compareTo(b) <= 0;
    }

    public static boolean eq(BigDecimal a, BigDecimal b) {
        return a.compareTo(b) == 0;
    }

    public static List<BigDecimal> splitAmount(BigDecimal amount, int count
            , BigDecimal min, BigDecimal max
            , BigDecimal step) {
        return SplitAmount.splitAmount(amount, count, min, max, step);
    }


    private static class SplitAmount {
        private static List<BigDecimal> splitAmount(BigDecimal amount, int count
                , BigDecimal min, BigDecimal max
                , BigDecimal step) {
            BigDecimal count0 = new BigDecimal(count);
            if (!isRight(amount, count0, min, max)) {
                throw new RuntimeException("not Right");
            }
            List<BigDecimal> list = new ArrayList<>();
            for (int i = 0; i < count; i++) {
                BigDecimal one = random(amount, min, max, count0.subtract(BigDecimal.valueOf(i)), step);
                list.add(one);
                amount = amount.subtract(one);
            }
            return list;
        }

        private static BigDecimal random(BigDecimal amount, BigDecimal min, BigDecimal max, BigDecimal count, BigDecimal step) {
            //数量为1，直接返回金额
            if (NumKit.eq(count, BigDecimal.ONE)) {
                return amount;
            }
            //如果最大金额和最小金额相等，直接返回金额
            if (NumKit.eq(min, max)) {
                return min;
            }
            //随机产生一个红包
            BigDecimal one = NumKit.randomNumber(min, NumKit.min(max, amount), step);
            BigDecimal newAmount = amount.subtract(one);
            //判断该种分配方案是否正确
            if (isRight(newAmount, count.subtract(BigDecimal.ONE), min, max)) {
                return one;
            } else {
                BigDecimal avg = NumKit.divide(newAmount, count.subtract(BigDecimal.ONE));
                if (NumKit.lt(avg, min)) {
                    //递归调用，修改最大
                    return random(amount, min, one, count, step);
                } else if (NumKit.gt(avg, max)) {
                    //递归调用，修改最小
                    return random(amount, one, max, count, step);
                }
            }
            return one;
        }

        private static boolean isRight(BigDecimal amount, BigDecimal count, BigDecimal min, BigDecimal max) {
            BigDecimal avg = NumKit.divide(amount, count);
            return avg.compareTo(min) >= 0 && avg.compareTo(max) <= 0;
        }
    }
}
